package com.gitee.wsl.ext.array

import com.gitee.wsl.ext.base.Arrays.arraycopy
import com.gitee.wsl.ext.base.SIZE
import com.gitee.wsl.ext.base.and
import kotlin.experimental.inv


/**
 * Size of long in bytes
 */
private val SIZEOF_LONG: Int = Long.SIZE / Byte.SIZE

/**
 * Bytewise binary increment/deincrement of long contained in byte array on given amount.
 *
 * @param value  - array of bytes containing long (length <= SIZEOF_LONG)
 * @param amount value will be incremented on (deincremented if negative)
 * @return array of bytes containing incremented long (length == SIZEOF_LONG)
 */
fun ByteArray.incrementBytes(amount: Long): ByteArray {
    var `val` = this
    if (`val`.size < SIZEOF_LONG) {
        // Hopefully this doesn't happen too often.
        val newvalue: ByteArray = if (`val`[0] < 0) {
            byteArrayOf(-1, -1, -1, -1, -1, -1, -1, -1)
        } else {
            ByteArray(SIZEOF_LONG)
        }
        arraycopy(`val`, 0, newvalue, newvalue.size - `val`.size, `val`.size)
        `val` = newvalue
    } else require(`val`.size <= SIZEOF_LONG) { "Increment Bytes - value too big: " + `val`.size }
    if (amount == 0L) return `val`
    if (`val`[0] < 0) {
        return binaryIncrementNeg(`val`, amount)
    }
    return binaryIncrementPos(`val`, amount)
}

/* increment/deincrement for positive value */
private fun binaryIncrementPos(value: ByteArray, amount: Long): ByteArray {
    var amo = amount
    var sign = 1
    if (amount < 0) {
        amo = -amount
        sign = -1
    }
    for (i in value.indices) {
        val cur = (amo.toInt() % 256) * sign
        amo = (amo shr 8)
        val `val` = value[value.size - i - 1].toInt() and 0x0ff
        var total = `val` + cur
        if (total > 255) {
            amo += sign.toLong()
            total %= 256
        } else if (total < 0) {
            amo -= sign.toLong()
        }
        value[value.size - i - 1] = total.toByte()
        if (amo == 0L) return value
    }
    return value
}

/* increment/deincrement for negative value */
private fun binaryIncrementNeg(value: ByteArray, amount: Long): ByteArray {
    var amo = amount
    var sign = 1
    if (amount < 0) {
        amo = -amount
        sign = -1
    }
    for (i in value.indices) {
        val cur = (amo.toInt() % 256) * sign
        amo = (amo shr 8)
        val `val`: Int = ((value[value.size - i - 1].inv()) and 0x0ff) + 1
        var total = cur - `val`
        if (total >= 0) {
            amo += sign.toLong()
        } else if (total < -256) {
            amo -= sign.toLong()
            total %= 256
        }
        value[value.size - i - 1] = total.toByte()
        if (amo == 0L) return value
    }
    return value
}